/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
/**
 * 
 */
package com.genia.toolbox.persistence.memory.criteria.field;

import java.util.List;

import com.genia.toolbox.persistence.basis.criteria.BasicCriteriaResult;
import com.genia.toolbox.persistence.exception.AggregateException;
import com.genia.toolbox.persistence.exception.FieldDefinitionException;

/**
 * TRhis field represent an arithmetic operation over an aggregate field.
 */
public class MemoryAggregateArithmeticOperationField
    extends AbstractMemoryField
{

  /**
   * this enum contains all the operations available.
   */
  public enum ArithmeticOperation
  {
    /**
     * the average.
     */
    AVG
    {
      /**
       * return the result of the arithmetic operation over the values.
       * 
       * @param values
       *          the values
       * @return the result of the operation
       */
      @Override
      protected Double operate(final double... values)
      {
        if(values.length == 0) {
          return null;
        }
        Double sum = SUM.operate(values);
        if(sum == null) {
          return null;
        }
        return sum / values.length;
      }
    },
    /**
     * the sum.
     */
    SUM
    {
      /**
       * return the result of the arithmetic operation over the values.
       * 
       * @param values
       *          the values
       * @return the result of the operation
       */
      @Override
      protected Double operate(final double... values)
      {
        if(values.length == 0) {
          return null;
        }
        double res = 0;
        for (final double value : values) {
          res += value;
        }
        return res;
      }
    },
    /**
     * the minimum.
     */
    MIN
    {
      /**
       * return the result of the arithmetic operation over the values.
       * 
       * @param values
       *          the values
       * @return the result of the operation
       */
      @Override
      protected Double operate(final double... values)
      {
        if(values.length == 0) {
          return null;
        }
        double res = values[0];
        for (final double value : values) {
          if (value < res) {
            res = value;
          }
        }
        return res;
      }
    },
    /**
     * the maximum.
     */
    MAX
    {
      /**
       * return the result of the arithmetic operation over the values.
       * 
       * @param values
       *          the values
       * @return the result of the operation
       */
      @Override
      protected Double operate(final double... values)
      {
        if(values.length == 0) {
          return null;
        }
        double res = values[0];
        for (final double value : values) {
          if (value > res) {
            res = value;
          }
        }
        return res;
      }
    };

    /**
     * return the result of the arithmetic operation over the values.
     * 
     * @param values
     *          the values
     * @return the result of the operation
     */
    protected abstract Double operate(double... values);
  }

  /**
   * the arithmetic operation of this field.
   */
  private final transient ArithmeticOperation operation;

  /**
   * the field to compute on.
   */
  private final transient AbstractMemoryField field;



  /**
   * the default constructor of this field.
   * 
   * @param operation
   *          the arithmetic operation of this field
   * @param field
   *          the field to compute on
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  public MemoryAggregateArithmeticOperationField(final ArithmeticOperation operation, final AbstractMemoryField field)
      throws AggregateException
  {
    if (field.isAggregate()) {
      throw new AggregateException();
    }
    this.operation = operation;
    this.field = field;
  }



  /**
   * Return the value of this field for the given line of result.
   * 
   * @param lines
   *          the line of result we compute the field on
   * @return the value of this field for the given line of result
   * @throws FieldDefinitionException
   *           when there is an error in the definition of the field
   * @throws AggregateException
   *           when there is an error with an aggregate definition
   */
  @Override
  public Double getValue(final List<BasicCriteriaResult<?>> lines)
      throws FieldDefinitionException, AggregateException
  {
    final double[] values = new double[lines.size()];
    int index = 0;
    for (final BasicCriteriaResult<?> line : lines) {
      final Number number = (Number) field.getSimpleValue(line);
      if (number == null) {
        return null;
      }
      values[index++] = number.doubleValue();
    }
    return operation.operate(values);
  }



  /**
   * returns if this <code>Field</code> is an aggregate.
   * 
   * @return <code>true</code> if this <code>Field</code> is an aggregate,
   *         <code>false</code> otherwise.
   */
  public boolean isAggregate()
  {
    return true;
  }

}
